package com.shop.cloud.upms.common.util;

import cn.hutool.core.io.FileUtil;
import com.github.odiszapc.nginxparser.*;
import com.shop.cloud.upms.common.entity.WebHost;
import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.List;

/**
 * nginx配置文件操作
 */
@Slf4j
@UtilityClass
public class NginxConfigUtil {

    static String reloadCmd= "nginx -s reload";

    /**
     * 执行单条命令
     */
    void execCmd(String cmd) throws IOException {
        Runtime run = Runtime.getRuntime();
        Process proc = null;
        BufferedReader br = null;
        InputStream in = null;
        try {
            proc = run.exec(cmd, null, null);
            in = proc.getInputStream();
            br = new BufferedReader(new InputStreamReader(in));

            String result;
            while ((result = br.readLine()) != null) {
                System.out.println("job result [" + result + "]");
            }

            proc.waitFor();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (proc != null)
                proc.destroy();
            if (in != null)
                in.close();
            if (br != null)
                br.close();
        }
    };
    /**
     * 通过域名查询server配置是否存在
     *
     * @param webHost
     * @param ngxConfigPath
     */
    public static boolean havServer(WebHost webHost, String ngxConfigPath) throws IOException {
        NgxConfig conf = NgxConfig.read(ngxConfigPath);
        List<NgxEntry> rtmpServers = conf.findAll(NgxConfig.BLOCK, "server");
        String serverName = getHostName(webHost.getHost());
        for (NgxEntry ngxEntry : rtmpServers) {
            NgxBlock ngxBlock = (NgxBlock) ngxEntry;
            String ngxParamServerName = ngxBlock.findParam("server_name").getValue();
            String ngxParamListen = ngxBlock.findParam("listen").getValue();
            if ("https".equals(webHost.getProtocol())
                    && "443 ssl".equals(ngxParamListen)
                    && serverName.equals(ngxParamServerName)) {
                return true;
            }
            if ("http".equals(webHost.getProtocol())
                    && "80".equals(ngxParamListen)
                    && serverName.equals(ngxParamServerName)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 添加nginx Server配置
     *
     * @param webHost
     * @param ngxConfigPath nginx配置文件路径
     * @return
     */
    public static boolean addServer(WebHost webHost, String ngxConfigPath) throws IOException {
        if (havServer(webHost, ngxConfigPath)) {
            throw new RuntimeException("该域名已配置"+webHost.getProtocol()+"协议，请先删除再添加");
        }

        NgxBlock ngxBlockWeb = new NgxBlock();
        ngxBlockWeb.addValue("server");
        if ("https".equals(webHost.getProtocol())) {
            NgxParam ngxParam = new NgxParam();
            ngxParam.addValue("listen 443 ssl");
            ngxBlockWeb.addEntry(ngxParam);
            ngxParam = new NgxParam();
            ngxParam.addValue(String.format("server_name %s", getHostName(webHost.getHost())));
            ngxBlockWeb.addEntry(ngxParam);

            String virtualServerName = webHost.getHost();
            String sslCertificatePath = String.format("%sssl/%s/%s/%s.pem", webHost.getHomeDir(), webHost.getTenantId(), virtualServerName, virtualServerName);
            if (!FileUtil.exist(sslCertificatePath)) {
                throw new RuntimeException("请先上传SSL证书");
            }
            ngxParam = new NgxParam();
            ngxParam.addValue(String.format("ssl_certificate %s", sslCertificatePath));
            ngxBlockWeb.addEntry(ngxParam);
            ngxParam = new NgxParam();
            String sslCertificateKeyPath = String.format("%sssl/%s/%s/%s.key", webHost.getHomeDir(), webHost.getTenantId(), virtualServerName, virtualServerName);
            if (!FileUtil.exist(sslCertificateKeyPath)) {
                throw new RuntimeException("请先上传SSL证书");
            }
            ngxParam.addValue(String.format("ssl_certificate_key %s", sslCertificateKeyPath));
            ngxBlockWeb.addEntry(ngxParam);

            ngxParam = new NgxParam();
            ngxParam.addValue("ssl_session_cache    shared:SSL:1m");
            ngxBlockWeb.addEntry(ngxParam);

            ngxParam = new NgxParam();
            ngxParam.addValue("ssl_session_timeout  5m");
            ngxBlockWeb.addEntry(ngxParam);

            ngxParam = new NgxParam();
            ngxParam.addValue("ssl_ciphers  HIGH:!aNULL:!MD5");
            ngxBlockWeb.addEntry(ngxParam);

            ngxParam = new NgxParam();
            ngxParam.addValue("ssl_prefer_server_ciphers  on");
            ngxBlockWeb.addEntry(ngxParam);
        } else {
            NgxParam ngxParam = new NgxParam();
            ngxParam.addValue("listen 80");
            ngxBlockWeb.addEntry(ngxParam);
            ngxParam = new NgxParam();
            ngxParam.addValue(String.format("server_name %s", getHostName(webHost.getHost())));
            ngxBlockWeb.addEntry(ngxParam);
        }

        NgxParam ngxParam = new NgxParam();
        ngxParam.addValue(String.format("root %s", webHost.getH5Path()));
        ngxBlockWeb.addEntry(ngxParam);

        NgxBlock ngxBlockLocation = new NgxBlock();
        ngxBlockLocation.addValue("location");
        ngxBlockLocation.addValue("/");
        ngxParam = new NgxParam();
        ngxParam.addValue("try_files $uri $uri/ /index.html");
        ngxBlockLocation.addEntry(ngxParam);
        ngxBlockWeb.addEntry(ngxBlockLocation);

        ngxBlockLocation = new NgxBlock();
        ngxBlockLocation.addValue("location");
        ngxBlockLocation.addValue("~* ^/(mall|mallapi|upms|code|live)");
        ngxParam = new NgxParam();
        ngxParam.addValue("proxy_pass http://127.0.0.1:9999");
        ngxBlockLocation.addEntry(ngxParam);
        ngxParam = new NgxParam();
        ngxParam.addValue("proxy_connect_timeout 15s");
        ngxBlockLocation.addEntry(ngxParam);
        ngxParam = new NgxParam();
        ngxParam.addValue("proxy_send_timeout 15s");
        ngxBlockLocation.addEntry(ngxParam);
        ngxParam = new NgxParam();
        ngxParam.addValue("proxy_read_timeout 15s");
        ngxBlockLocation.addEntry(ngxParam);
        ngxParam = new NgxParam();
        ngxParam.addValue("proxy_set_header X-Real-IP $remote_addr");
        ngxBlockLocation.addEntry(ngxParam);
        ngxParam = new NgxParam();
        ngxParam.addValue("proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for");
        ngxBlockLocation.addEntry(ngxParam);
        ngxParam = new NgxParam();
        ngxParam.addValue("proxy_http_version 1.1");
        ngxBlockLocation.addEntry(ngxParam);
        ngxParam = new NgxParam();
        ngxParam.addValue("proxy_set_header Upgrade $http_upgrade");
        ngxBlockLocation.addEntry(ngxParam);
        ngxParam = new NgxParam();
        ngxParam.addValue("proxy_set_header Connection \"upgrade\"");
        ngxBlockLocation.addEntry(ngxParam);
        ngxBlockWeb.addEntry(ngxBlockLocation);

//        ngxBlockLocation = new NgxBlock();
//        ngxBlockLocation.addValue("if ($request_uri ~ \"/actuator\")");
//        ngxParam = new NgxParam();
//        ngxParam.addValue("return 403");
//        ngxBlockLocation.addEntry(ngxParam);
//        ngxBlockWeb.addEntry(ngxBlockLocation);

        NgxConfig ngxConfig = NgxConfig.read(ngxConfigPath);
        ngxConfig.addEntry(ngxBlockWeb);

        String content = new NgxDumper(ngxConfig).dump();
        log.info("{}", content);
        //覆盖写入文件
        FileUtil.writeUtf8String(content, ngxConfigPath);
        //重载nginx配置文件
        execCmd(reloadCmd);
        return true;
    }

    /**
     * 删除nginx Server配置
     *
     * @param webHost
     * @param ngxConfigPath
     * @return
     */
    public static boolean delServer(WebHost webHost, String ngxConfigPath) throws IOException {
        NgxConfig ngxConfig = NgxConfig.read(ngxConfigPath);
        List<NgxEntry> serverList = ngxConfig.findAll(NgxConfig.BLOCK, "server");
        String serverName = getHostName(webHost.getHost());
        for (NgxEntry ngxEntry : serverList) {
            NgxBlock ngxBlock = (NgxBlock) ngxEntry;
            String ngxParamServerName = ngxBlock.findParam("server_name").getValue();
            String ngxParamListen = ngxBlock.findParam("listen").getValue();
            if ("https".equals(webHost.getProtocol())
                    && "443 ssl".equals(ngxParamListen)
                    && serverName.equals(ngxParamServerName)) {
                ngxConfig.remove(ngxBlock);
            }
            if ("http".equals(webHost.getProtocol())
                    && "80".equals(ngxParamListen)
                    && serverName.equals(ngxParamServerName)) {
                ngxConfig.remove(ngxBlock);
            }
        }
        String content = new NgxDumper(ngxConfig).dump();
        log.info("{}", content);
        //覆盖写入文件
        FileUtil.writeUtf8String(content, ngxConfigPath);
        //重载nginx配置文件
        execCmd(reloadCmd);
        return true;
    }

    /**
     * http转https
     * @param webHost
     * @param ngxConfigPath
     */
    public static boolean httpToHttps(WebHost webHost, String ngxConfigPath) throws IOException {
        //查询是否已配https协议
        webHost.setProtocol("https");
        if (!havServer(webHost, ngxConfigPath)) {
            throw new RuntimeException("请先配置https协议");
        }
        //先删除原有的http
        webHost.setProtocol("http");
        delServer(webHost, ngxConfigPath);
        NgxBlock ngxBlockWeb = new NgxBlock();
        ngxBlockWeb.addValue("server");

        NgxParam ngxParam = new NgxParam();
        ngxParam.addValue("listen 80");
        ngxBlockWeb.addEntry(ngxParam);

        ngxParam = new NgxParam();
        ngxParam.addValue(String.format("server_name %s", getHostName(webHost.getHost())));
        ngxBlockWeb.addEntry(ngxParam);

        ngxParam = new NgxParam();
        ngxParam.addValue(String.format("return 301 https://%s$request_uri", webHost.getHost()));
        ngxBlockWeb.addEntry(ngxParam);

        NgxConfig ngxConfig = NgxConfig.read(ngxConfigPath);
        ngxConfig.addEntry(ngxBlockWeb);

        String content = new NgxDumper(ngxConfig).dump();
        log.info("{}", content);
        //覆盖写入文件
        FileUtil.writeUtf8String(content, ngxConfigPath);
        //重载nginx配置文件
        execCmd(reloadCmd);
        return true;
    }

    public static String getHostName(String host){
        return String.format("%s www.%s", host, host);
    }

    public static void main(String[] args) throws IOException {
        WebHost webHost = new WebHost();
        webHost.setHost("test.bjyq.com");
        webHost.setProtocol("https");
        webHost.setTenantId("1");
        webHost.setHomeDir("C:/bjyq-file/");
        webHost.setH5Path("/mnt/install/bjyq-pro/h5-pc/");
//        addServer(webHost, "C:/nginx/conf.d/1.conf");
        delServer(webHost, "C:/nginx/conf.d/1.conf");
//        httpToHttps(webHost, "C:/nginx/conf.d/1.conf");
    }
}
